home *** CD-ROM | disk | FTP | other *** search
-
- KingFisher Database Server (KFServer)
- Application Programming Interface (API)
- Copyright © 1993,1994,1995 Udo K. Schuermann
- All rights reserved
-
-
-
- ---------------------------------------------------------------------------
- INTRODUCTION
- ---------------------------------------------------------------------------
-
- The KFServer's API is based on the Amiga's Exec messages. This is a very
- efficient means of transporting potentially enormous amounts of information
- between multiple tasks. The only way for a client application to talk to
- the KFServer is by sending messages to the KFServer and waiting for these
- to be returned.
-
- All the gory details of this process are handled for you by the "kf-api.c"
- file, providing you with simple functions to setup messages, fill the
- parameter fields, send the message, wait for a reply from the KFServer, and
- then return you the necessary results.
- The "kf-api.c" functions were developed using SAS/C 6.55 but should
- be easily compiled with other compilers, too.
-
-
-
- ---------------------------------------------------------------------------
- GETTING STARTED
- ---------------------------------------------------------------------------
-
- The first thing you should do is compile "kf-api.c" to produce "kf-api.o"
- (i.e. don't link this file with anything; you'll use the functions in the
- object (.o) file later to link with your project.) Once you have this file
- compiled you are ready to begin your project!
-
- The basic outline of a KFServer Client Application is this:
-
- Handle = KFLogin( "Identifier" )
- IF (Handle = NULL) THEN EXIT( "Server Not Running" )
-
- REPEAT
- Issue command
- IF Successful THEN ECHO Results
- UNTIL Finished
-
- KFLogout( Handle )
-
- 1. An application must always login to the server at least once. The
- "Handle" is the identifier passed to every API function, to identify
- this "connection" to the server. If you examine the "ReOrder.c" tool,
- you will notice that TWO (2) handles are obtained, one for the input
- database, and one for the output database.
- There is no limit (except with the unregistered version) to the
- number of handles your software may obtain. Each such handle is
- associated with a database and maintains its own, distinct set of
- parameters. This provides simultaneous access to multiple databases.
-
- 2. The KFServer is a single-threaded application, which means it processes
- each request as it comes in and goes on to the next request only when
- the previous one has completed and has been returned to the client.
- This means that certain lengthy operations (such as a database reindex
- operation) could tie up the server for several minutes, thereby blocking
- all access by other clients.
- To somewhat lessen the impact of this potentially very frustrating
- blockage, these lengthy operations return to the caller periodically
- with the implication that the function is called again at once until the
- KFServer finally returns with an "all done," whereupon the function is
- completed.
- These interruptions provide brief pauses during which other clients
- may get a few requests processed. It is also recommended that a client
- lower its priority by one before embarking on such a lengthy procedure.
- This can greatly improve response time for other clients, should there
- be any!
- The "kf-api.c" function for reindexing, for example, takes care of
- the repeated calling, so there is no need for you to directly concern
- yourself with this aspect unless you should wonder why your other client
- software is slowing down dramatically during a reindexing operation.
-
- 3. Always call KFLogout() on a handle you have obtained. If you are using
- SAS/C 6.50+ and the kf-api.c file has the #define USEAUTO enabled (which
- it is, by default) then the kf-api.c will automatically clear all open
- handles for you when you exit, but it is not recommended to rely on
- this.
-
-
-
- ---------------------------------------------------------------------------
- EXAMPLE OF USING THE KF-API.C
- ---------------------------------------------------------------------------
-
- Here is a very small application you can play with:
-
- #include <stdio.h>
- #include "kf-api.h"
- main() {
- struct KFMsg *HANDLE = KFLogin("My Mini Project");
- if( HANDLE ) {
- unsigned long MaxClients;
- char *DefaultDB;
-
- MaxClients = KFMaxClients( HANDLE );
- printf("This KFServer supports %ld clients,\n",MaxClients);
-
- DefaultDB = KFCurDatabaseName( HANDLE );
- printf("and serves \"%s\" as the default database.\n",DefaultDB);
- KFLogout( HANDLE );
- /* at this time HANDLE is invalid */
- } else
- printf("Cannot login!\n");
- } /* main() */
-
- This application makes use of the following API functions:
-
- KFLogin()
- KFLogout()
- KFMaxClients()
- KFCurDatabaseName()
-
- These functions are described in detail in the section below.
-
-
-
- ---------------------------------------------------------------------------
- FUNCTIONS IN THE API:
- ---------------------------------------------------------------------------
-
- struct KFMsg *KFLogin( char *Identifier );
-
- This should almost always be the first function you call. It
- returns you the handle which you use in all subsequent calls. The
- parameter to this function should be some meaningful identification
- and may even be NULL. It is highly recommended to somehow identify
- your application, as in the example above. The KFServer does NOT
- make a copy of this string, so the address must continue to live as
- long as the handle is active.
-
- If the function returns NULL, then login failed. There is more
- than one reason for this. As there is no message with error field
- that is returned by this function, examine (but do not modify!) the
- variables
-
- UWORD AsyncErr
- and
- char *AsyncMsg
-
- for an error code and some additional explanation. The most likely
- causes of a KFLogin() call failing is that either the KFServer is
- out of handles (never the case with the registered version) or the
- KFServer is not running and could not be started successfully.
-
-
-
- BOOL KFLogout( struct KFMsg *Msg );
-
- Always call this function with every handle obtained through the
- KFLogin() call. It is legal to call this function with a NULL
- handle, but the function will return FALSE in that case.
-
- The function returns TRUE if the logout succeeded. In either case,
- the handle is invalid after this call.
-
-
-
- BOOL KFStatus( struct KFMsg *Msg );
-
- Obtains information about the currently attached client, including
- the current database in use, the current record position in the
- database, the version of the running server, etc. This information
- is returned in the handle's .BParam field.
-
- EX: if( KFStatus( HANDLE ) )
- printf("%s\n",HANDLE->BParam);
-
- NOTE: In a future version, this function will not require a prior
- KFLogin() and may be used to obtain some simple information
- about the KFServer (which will be started if not running.)
-
-
-
- ULONG KFMaxClients( struct KFMsg *Msg );
-
- Obtains the maximum number of clients that the currently running
- KFServer supports. Depending on how many handles have already been
- handed out by the KFServer to various clients, the actual number of
- available handles may be less than the maximum. This should be of
- concern only with unregistered versions.
-
- EX: ULONG MaxClients = KFMaxClients( HANDLE );
-
- NOTE: It is LEGAL to call KFMaxClients() with a NULL handle!
- This will actually create a temporary handle and call the
- KFServer with that. If the KFServer is configured with the
- "keep-running=no" parameter, however, the KFServer will
- NOT, in this case, terminate again, but remain operational
- with 0 clients attached. Auto termination of the KFServer
- functions only during an actual logout procedure.
-
-
-
- ULONG KFCurFish( struct KFMsg *Msg );
-
- Obtains the current position in the database which is a number 1 or
- greater. If this function returns 0, then the database is empty
- and no record or record-specific data may be obtained from it.
-
- EX: ULONG CurFish = KFCurFish( HANDLE );
- if( CurFish == 0 )
- printf("We have no data!\n");
- else
- printf("Record #%ld is the current one.\n",CurFish);
-
- NOTE: The .FParam field is also filled with the current flags
- (see KFCurFlags() function below.)
-
-
-
- UWORD KFCurFlags( struct KFMsg *Msg );
-
- Obtains the flags of the current record in the database. Eight of
- these flags are pre-defined, while another eight are application or
- user-specific.
-
- UWORD CurFlags = KFCurFlags( HANDLE );
- printf("System flags: %02x, User flags: %02x\n",
- CurFlags >> 8,
- CurFlags & 0x0f);
-
- NOTE: The KFCurFish() command (see above) provides the current
- flags as a sideeffect in the handle's .FParam field.
-
-
-
- ULONG KFCurDisk( struct KFMsg *Msg );
-
- Obtains the current disk number with which the current record is
- associated. Note, that a single-volume software collection, such
- as found on a CD-ROM, is likely to be organized as a single disk,
- wherefore the disk number is always 1.
-
-
-
- char *KFCurDatabaseName( struct KFMsg *Msg );
-
- Obtains the .kfdb filename of the current database.
-
- NOTE: The value returned is actually the same pointer as the
- handle's .Buffer, wherefore the value will not survive the
- next use of the handle when passed to the KFServer again.
- If you require this value, then strcpy() it to a different
- buffer!
-
-
-
- ULONG KFCurDatabaseSize( struct KFMsg *Msg );
-
- Returns the number of records in the current database. If the
- function returns 0, then the database is empty.
-
-
-
- BOOL KFSelectFish( struct KFMsg *Msg, ULONG FishNumber );
-
- Selects a different record in the database for future operations.
- The record (FishNumber) given must be 1 or greater, and must not
- exceed the total number of records in the database (as returned by
- a call to KFCurDatabaseSize().)
-
-
-
- ULONG KFNextVersion( struct KFMsg *Msg );
- ULONG KFPrevVersion( struct KFMsg *Msg );
-
- Returns the record number of the next/previous version as stored in
- the current database's index. If this function returns 0, then no
- next/previous version is known.
-
-
-
- BOOL KFListDatabases( struct KFMsg *Msg );
-
- This call returns a pointer to the handle's .Buffer where the
- KFServer has stored a list of all known .kfdb files, along with the
- long, human-readable descriptions found within these. The format
- of the buffer is:
-
- Descriptive database name
- \1
- .kfdb name
- \n
-
- This is repeated for each existing .kfdb file and the buffer is
- then terminated with a \0. Notice that \1 is an ASCII 01 (^A)
- symbol, and \n is a newline.
-
- Here is an example without the linebreaks used above to make the
- components stand out more distinctly:
-
- Fresh Fish® Database\1FreshFish.kfdb\n
- Original Fish Disks\11000Fish.kfdb\n
- \0
-
-
-
- BOOL KFSelectDatabase( struct KFMsg *Msg, char *Filename );
-
- Ask the KFServer to close the current database and switch to the
- new database whose name (a .kfdb file) is given by the Filename
- parameter. The 1st record in the database becomes the current
- record. Clients may wish to restore an old, saved position with a
- KFSelectFish() call or directly load a record with a KFGetFish()
- call.
-
- The command returns FALSE if the new database could not be opened,
- in which case the current database is not closed. If the function
- returns TRUE, the .BParam contains a bitmask to indicate the R/W
- status of various database components. If the entire value is 0,
- (equal to a boolean FALSE) then no portion of the database can be
- modified. AND'ing the value with the following gives details on
- each component:
- x AND 1 The main database index
- x AND 2 The quick index
- x AND 4 The database records
- If the database is not writable, any additions to it, or changes
- to the index (i.e. flags) can, and most likely will, cause error
- messages from the server when the database is closed.
-
- NOTE: The Filename must be terminated either by a \0 or \n.
- This makes it easier to point directly at a name in the
- .Buffer (see KFListDatabases() command, above.)
- NOTE: In previous versions the .BParam value was a simple
- boolean to indicate read/write status of the entire
- database based solely on the main index, which was not
- always correct, especially when only the main index
- resided on writable medium.
-
-
-
- BOOL KFGetFish( struct KFMsg *Msg, ULONG FishNumber );
-
- Retrieves a specific record from the current database. If the
- FishNumber is 0 (rather than a valid record number 1 or greater)
- then the current record is retrieved, whichever that may be.
-
- NOTE: The .BParam field returns the actual record number
- retrieved (useful when called with a record of 0)
- The .DParam field returns the disk with which this record
- is associated
- The .FParam field returns the record's associated flags
- (see the KFCurFlags() command)
- The .FInfo field returns situational information on this
- record, indicating if previous or next versions
- exist, or if this is the first or last record or
- disk in the database. This field represents a mask
- of bits, which are defined in the kf.h file.
-
-
-
- BOOL KFSetFlag( struct KFMsg *Msg, UWORD Mask );
- BOOL KFClrFlag( struct KFMsg *Msg, UWORD Mask );
-
- Sets/clears flags in the indicated Mask. A Mask of 0xffff would
- set/clear all flags. A Mask of 0x0000 has no effect at all. You
- should not call this function if the call to KFSelectDatabase()
- returned a 0 in the .BParam field, indicating that the database
- cannot be modified.
-
-
-
- BOOL KFFindFlag( struct KFMsg *Msg, BOOL Forward, UWORD MatchMask, UWORD AvoidMask );
-
- Starting with the current record, the KFServer will make current
- the next or previous record matching the given masks. If
- successful, the function returns TRUE and the new record number is
- returned in the .BParam field.
-
- An AvoidMask of 0xffff will NEVER produce a match (as all records
- with any flag are avoided), while a MatchMask of 0x0000 will
- likewise NEVER produce a match.
-
- The Forward variable determines the direction in which the search
- is performed. The KFServer can search thousands of records in a
- fraction of a second.
-
-
-
- BOOL KFGetDisk( struct KFMsg *Msg, ULONG DiskNumber );
-
- Retrieves the first record associated with the indicated disk,
- provided this disk exists (in which case the function returns TRUE)
- and an implicit KFGetFish() call has been performed (see above.)
-
-
-
- BOOL KFNextFish( struct KFMsg *Msg );
- BOOL KFPrevFish( struct KFMsg *Msg );
-
- Moves to the next/previous record in the database, returning TRUE
- if such a record existed. No retrieval of data is performed.
-
-
-
- BOOL KFParseFile( struct KFMsg *Msg, char *Filename );
- BOOL KFEndParsing( struct KFMsg *Msg );
-
- KFParseFile() is called both to begin and to continue parsing a
- file for records in the Product Info Specification format. Each
- time the function returns either TRUE or FALSE. If TRUE, the
- .Buffer contains a record extracted from the file, which may be
- passed to the KFAddFish() function if so desired.
-
- Once this function returns FALSE, or the .Error field contains
- kfeNOMORE, the file has been completely parsed for all records.
- Only if you wish to terminate parsing before the file has been
- completely scanned, may you call KFEndParsing().
-
- EX: BOOL HaveAdded = FALSE;
- for(;;) {
- if( KFParseFile( HANDLE, "myfile" ) ) {
- printf("%s\n\n***** Add this record? (Y/N/Q) ",
- HANDLE->Buffer);
- /* get Yes/No/Quit response */
- if( Yes )
- if( KFAddFish( HANDLE, 1, 0x0000, -1, -1 ))
- HaveAdded = TRUE;
- else {
- printf("Cannot add to the database!\n");
- break;
- }
- else
- if( No )
- continue;
- else
- if( Quit ) {
- break;
- }
- } else {
- HaveAdded = FALSE; /* KFServer knows it's done */
- break;
- }
- } /* for */
- if( HaveAdded )
- KFEndAdding( HANDLE );
-
-
-
- BOOL KFAddFish( struct KFMsg *Msg, UWORD Disk, UWORD Flags, LONG PrevVersion, LONG NextVersion );
- BOOL KFEndAdding( struct KFMsg *Msg );
-
- When KFAddFish() is first called, it attempts to lock the current
- database for exclusive access. If more than one handle to this
- database has been given out (even if both are in your "possession")
- the call will fail.
-
- Once you have added all the records you wish to add, call the
- KFEndAdding() function to release the database again to everyone
- else.
-
- For an example of this function, see the source code example above.
-
-
-
- BOOL KFReindex( struct KFMsg *Msg );
-
- This function attempts to lock the current database for exclusive
- access. If it succeeds, it will proceed to reindex the database,
- thereby rebuilding all related index files based solely on the data
- found in the database files.
-
- Once the function returns, the database has been released again and
- the index is up to date. Notice that this function may require
- much time to perform its duty, during which other database clients
- may experience EXTREME drop in performance. For this reason, it is
- STRONGLY RECOMMENDED to lower your process priority before calling
- this function, and raise it again once completed.
-
- Previously mentioned problems with the reindexing operation have
- been corrected, to the best of my knowledge and experience.
-
-
- ---------------------------------------------------------------------------
- HINTS AND TIPS
- ---------------------------------------------------------------------------
-
- 1. Use the functions provided by the API as much as possible. It is LEGAL
- to access the fields of the handle, so long as their use is documented
- here. Do not rely on apparently reliable behavior if no mention is made
- of it here. If you must know, ask me. It may always be an omission.
- If you need something added, let me know and I will see what I can do.
-
- 2. You may modify the KFAPIServerName[128] array to provide a different
- name of the KFServer binary, perhaps with a full path added. This is
- the file which the API attempts to start when the KFServer's message
- port cannot be located.
- KingFisher copies the value of the SERVERNAME tooltype to this
- variable.
-
- If you wish to suppress startup messages and console windows for the
- cleanest look, set the KFAPISilentRun to TRUE.
- KingFisher does this if it finds the NOOUTPUT tooltype.
-
- Never EVER rely on anything else in the kf-api.c file, as nothing else
- will be guaranteed and behavior may dramatically change.
-
- 3. If so desired, you may use the CallServer() function, which expects a
- fully initialized handle. The use of CallServer() is documented in the
- "kf-api.c" by itself and all the functions that use it.
-
- 4. If you experience problems, please let me know. The more detail you can
- give, the more likely it is that I can help you.
-
- 5. While the file above may not always be up to date, you can get more
- detail (although fewer examples) on the exact message port interface
- from the kf.h file.
-
-
- Enjoy!
-
- ._. Udo Schuermann Snail mail: 7022 Hanover Parkway, Apt. C2
- ( ) walrus@wam.umd.edu Greenbelt, MD 20770-2049
-
- #EOT
-